Visualization

Results generated by LLM will be shown in this page

Click to show code
import pandas as pd
import folium
from pathlib import Path
import altair as alt
import plotly.io as pio
import plotly.express as px
import numpy as np
import matplotlib.pyplot as plt
Click to show code
pio.renderers.default = "notebook"
Click to show code

base_dir = Path(r"street_samples_250m_after_seg")

csv_path = base_dir / "street_points_250m_with_seg_text.csv"
df = pd.read_csv(csv_path)

print("Loaded rows:", len(df))
print("Columns:", df.columns.tolist())


df.head(3)
Loaded rows: 171
Columns: ['city', 'street', 'segment_id', 'point_id', 'distance_m', 'lon', 'lat', 'heading_road', 'heading_sidewalk_right', 'heading_sidewalk_left', 'image_file', 'image_path', 'gsv_status', 'gsv_url_sidewalk', 'abs_image_path', 'building_frac', 'sky_frac', 'road_frac', 'tree_frac', 'grass_frac', 'sidewalk_frac', 'seg_vis_file', 'seg_vis_path', 'seg_features_text', 'sidewalk_quality_and_walkability_score', 'greenery_score', 'building_enclosure_score', 'Comfort_and_perceived_safety_score', 'total_score', 'gpt_comment']
city street segment_id point_id distance_m lon lat heading_road heading_sidewalk_right heading_sidewalk_left ... sidewalk_frac seg_vis_file seg_vis_path seg_features_text sidewalk_quality_and_walkability_score greenery_score building_enclosure_score Comfort_and_perceived_safety_score total_score gpt_comment
0 Philadelphia, Pennsylvania, USA Chestnut Street 0 46 11500 -75.145108 39.948523 99.521202 189.521202 9.521202 ... 0.326274 seg_idx0126_seg0_pt46_h190.jpg /content/drive/MyDrive/Colab_Notebooks/final/s... According to a semantic segmentation model, th... 23 21 20 22 86 With about 32.6% sidewalks and no visible road...
1 Philadelphia, Pennsylvania, USA Chestnut Street 0 41 10250 -75.156190 39.949909 99.191861 189.191861 9.191861 ... 0.398777 seg_idx0121_seg0_pt41_h189.jpg /content/drive/MyDrive/Colab_Notebooks/final/s... According to a semantic segmentation model, th... 22 18 21 20 81 With about 39.9% sidewalks and almost no visib...
2 Philadelphia, Pennsylvania, USA Chestnut Street 0 44 11000 -75.149538 39.949089 99.181031 189.181031 9.181031 ... 0.280364 seg_idx0124_seg0_pt44_h189.jpg /content/drive/MyDrive/Colab_Notebooks/final/s... According to a semantic segmentation model, th... 21 20 19 21 81 With about 28% of the scene as paved pedestria...

3 rows × 30 columns

Average Streetscape Composition and Differences Across Market, Chestnut, and Walnut Streets

Click to show code

feature_cols = [
    'street',
    'building_frac',
    'road_frac',
    'tree_frac',
    'sidewalk_frac',
    'sky_frac',
    'grass_frac'
]
df_features = df[feature_cols]


df_avg_features = df_features.groupby('street').mean().reset_index()


feature_mapping = {
    'building_frac': 'Building',
    'road_frac': 'Roadway',
    'tree_frac': 'Trees',
    'sidewalk_frac': 'Sidewalk',
    'sky_frac': 'Sky',
    'grass_frac': 'Grass'
}

df_long_features = pd.melt(
    df_avg_features,
    id_vars=['street'],

    value_vars=feature_mapping.keys(),
    var_name='Feature',
    value_name='Average Percentage'
)


df_long_features['Feature'] = df_long_features['Feature'].map(feature_mapping)



chart_composition_en = alt.Chart(df_long_features).encode(
    x=alt.X('Average Percentage:Q', axis=alt.Axis(format='.0%', title='Average Percentage')),
    y=alt.Y('street:N', title='Street'),
    color=alt.Color('Feature:N', title='Landscape Feature'),
    tooltip=[
        alt.Tooltip('street', title='Street'), 
        alt.Tooltip('Feature', title='Feature'), 
        alt.Tooltip('Average Percentage', format='.1%', title='Average Percentage')
    ]
).properties(
    title='Average Landscape Feature Composition by Street',
    # size
    width=1200,
    height=200
).mark_bar().interactive() 

chart_composition_en

Check whether the AI can incorporate perception and influence the scores for each component.

Click to show code

df["greenery_frac"] = df["tree_frac"] + df["grass_frac"]


pairs = [
    (
        "sidewalk_frac",
        "sidewalk_quality_and_walkability_score",
        "Sidewalk fraction",
        "Sidewalk quality & walkability score",
    ),
    (
        "greenery_frac",
        "greenery_score",
        "Greenery fraction (trees + grass)",
        "Greenery score",
    ),
    (
        "building_frac",
        "building_enclosure_score",
        "Building fraction",
        "Building enclosure score",
    ),
    (
        "road_frac",
        "Comfort_and_perceived_safety_score",
        "Road fraction",
        "Comfort & perceived safety score",
    ),
]


new_color = '#17becf'

fig, axes = plt.subplots(2, 2, figsize=(10, 8))

# 4. 循环生成散点图
for i, (x_col, y_col, x_label, y_label) in enumerate(pairs):
    # 根据循环索引 i 确定当前子图的位置 (row, col)
    row = i // 2
    col = i % 2
    ax = axes[row, col]

    # 取数据并去掉缺失
    x = df[x_col].values
    y = df[y_col].values
    mask = ~np.isnan(x) & ~np.isnan(y)
    x = x[mask]
    y = y[mask]

    # 计算相关系数
    if len(x) > 1:
        corr = np.corrcoef(x, y)[0, 1]
    else:
        corr = np.nan

    # 画散点,使用新颜色
    ax.scatter(x, y, alpha=0.7, color=new_color, edgecolors='grey', linewidths=0.5)

    # 加一条简单的线性拟合线(可选)
    if len(x) > 1:
        coef = np.polyfit(x, y, 1)
        x_line = np.linspace(x.min(), x.max(), 100)
        y_line = coef[0] * x_line + coef[1]
        ax.plot(x_line, y_line, color='red', linestyle='--', linewidth=1.2)

    ax.set_xlabel(x_label)
    ax.set_ylabel(y_label)
    ax.set_title(f"{y_label} vs {x_label} (r = {corr:.2f})")
    ax.set_ylim(0, 25)

plt.tight_layout()

Average total score and components score

Click to show code
#  Data Preprocessing: Select score columns and calculate the average
score_columns = [
    'street',
    'total_score',
    'sidewalk_quality_and_walkability_score',
    'greenery_score',
    'building_enclosure_score',
    'Comfort_and_perceived_safety_score'
]
df_scores = df[score_columns]
df_avg_scores = df_scores.groupby('street').mean().reset_index()

# Rename columns to clean English labels for plotting clarity
df_avg_scores.columns = [
    'Street',
    'Total Score',
    'Sidewalk/Walkability Score',
    'Greenery Score',
    'Building Enclosure Score',
    'Comfort/Safety Score'
]

# Chart 1: Average Total Score Comparison 

df_avg_scores_sorted = df_avg_scores.sort_values('Total Score', ascending=False)

chart_total = alt.Chart(df_avg_scores_sorted).encode(
    y=alt.Y('Street:N', title='Street', sort='-x'),
    x=alt.X(
        'Total Score:Q',
        title='Average Total Score',
        scale=alt.Scale(domain=[0, 100])  
    ),
    color=alt.Color('Street:N', legend=alt.Legend(title="Street Name")),
    tooltip=['Street', alt.Tooltip('Total Score', title='Avg Total Score', format='.1f')]
).properties(
    title='Average Total Score by Street',
    width=1265
).mark_bar()


text_total = chart_total.mark_text(
    align='left',
    baseline='middle',
    dx=3
).encode(
    text=alt.Text('Total Score:Q', format='.1f'),
    color=alt.value('black')
)

final_chart_total = (chart_total + text_total)


# Chart 2: Component Score Comparison (Grouped Bar Chart)

# 这里只放 4 个 component 维度(每个满分 25)
score_name_mapping = {
    'Sidewalk/Walkability Score': 'Sidewalk/Walkability',
    'Greenery Score': 'Greenery',
    'Building Enclosure Score': 'Building Enclosure',
    'Comfort/Safety Score': 'Comfort/Safety'
}

# Reshape the data from wide format to long format
df_long = pd.melt(
    df_avg_scores,
    id_vars=['Street'],
    value_vars=score_name_mapping.keys(),
    var_name='Score Type',
    value_name='Average Score'
)

# Apply mapping to the 'Score Type' column for cleaner facet labels
df_long['Score Type'] = df_long['Score Type'].map(score_name_mapping)

# Create the grouped bar chart
chart_component = alt.Chart(df_long).encode(
    x=alt.X(
        'Average Score:Q',
        title='Average Score (0–25)',
        scale=alt.Scale(domain=[0, 25])   # 统一 0–25
    ),
    y=alt.Y('Street:N', title='Street'),
    color=alt.Color('Score Type:N', title='Score Type'),
    column=alt.Column(
        'Score Type:N',
        title='Average Component Score Comparison'
    ),
    tooltip=['Street', 'Score Type', alt.Tooltip('Average Score', title='Average Score', format='.1f')]
).properties(
    title=alt.TitleParams("")
).mark_bar()

# 把总分图叠在上面,组件图在下面
combined_chart = alt.vconcat(
    final_chart_total,
    chart_component,
    spacing=20             # 上下留一点空隙
).resolve_scale(
    x='independent'        # 上面 0–100,下面 0–25
).configure_header(
    titleFontSize=14,
    labelFontSize=12
)

combined_chart

Spatial Distribution of scores

Click to show code
score_cols = [
    "sidewalk_quality",
    "greenery_score",
    "building_enclosure_score",
    "Comfort_and_perceived_safety_score",
    "total_score"
]
Click to show code

score_cols_original = [
    'sidewalk_quality_and_walkability_score', 
    'greenery_score', 
    'building_enclosure_score', 
    'Comfort_and_perceived_safety_score', 
    'total_score'
]

score_cols_scaled = []
for col in score_cols_original:
    new_col = f'{col}_squared'

    df[new_col] = df[col] ** 2
    score_cols_scaled.append(new_col)


score_cols_to_plot = score_cols_original + score_cols_scaled
Click to show code

# 将最大点尺寸设置为 100(适应平方后的更大数值)
NEW_SIZE_MAX = 30

for col_scaled in score_cols_scaled:
    # 原始分数作为颜色编码和悬停显示项
    col_original = col_scaled.replace('_squared', '')
    
    print(f"Generating map for: {col_original}")

    fig = px.scatter_map(
        df,
        lat="lat",
        lon="lon",
        # 颜色仍然使用原始分数
        color=col_original,
        color_continuous_scale="Viridis",
        # 尺寸使用平方后的分数,变化更灵敏
        size=col_scaled, 
        size_max=NEW_SIZE_MAX, 
        zoom=13,
        map_style="carto-positron",
        
        hover_data={
            col_original: True,         
            col_scaled: False,
            "gsv_url_sidewalk": False,   
            "lat": False,               
            "lon": False,               
            "street": False,            
            "gpt_comment": False       
        }
    )

    fig.update_layout(
        title=f"Map of {col_original} ",
        margin={"r":0,"t":40,"l":0,"b":0},
        height=600
    )
    fig.show() 
Generating map for: sidewalk_quality_and_walkability_score
Generating map for: greenery_score
Generating map for: building_enclosure_score
Generating map for: Comfort_and_perceived_safety_score
Generating map for: total_score

Check the images of highest and lowest scores

Look at the top-scoring points

Click to show code
import folium
import base64

best3 = df.nlargest(5, "total_score")

# 2. 计算地图中心位置
center_lat = best3["lat"].mean()
center_lon = best3["lon"].mean()

# 3. 创建一个“更长 + zoom 更小”的地图
m = folium.Map(
    location=[center_lat, center_lon],
    zoom_start=13,          # <<< 缩小一点点(推荐)
    width=750,              # <<< 地图更长
    height=450,             # <<< 高度略小
    tiles="CartoDB Positron"  # 浅色底图
)

# 4. 添加三个点 + 图片 popup
for idx, row in best3.iterrows():

    # 将图片编码为 base64
    with open(row["abs_image_path"], "rb") as f:
        img_base64 = base64.b64encode(f.read()).decode()

    html = f"""
    <h4>Point ID: {row['point_id']}</h4>
    <p>Total Score: {row['total_score']}</p>
    <img src="data:image/jpeg;base64,{img_base64}" width="300">
    """

    iframe = folium.IFrame(html=html, width=330, height=330)
    popup = folium.Popup(iframe, max_width=400)

    folium.CircleMarker(
        location=[row["lat"], row["lon"]],
        radius=7,
        color="#3478f6",
        fill=True,
        fill_color="#5b9bff",
        fill_opacity=0.9,
        tooltip=f"Score: {row['total_score']}",
        popup=popup
    ).add_to(m)

m
Make this Notebook Trusted to load map: File -> Trust Notebook
Click to show code
from IPython.display import HTML, display

best3 = df.nlargest(5, "total_score").copy()

for idx, row in best3.iterrows():
    
    img_html = f"""
    <div style="flex: 1; padding-right:20px;">
        <img src="{row['abs_image_path']}" width="420" style="border-radius:8px;"/>
    </div>
    """
    
    table_html = f"""
    <div style="flex: 1.2; font-size:14px;">
        <h3 style="margin-top:0;">Total Score: {row['total_score']}</h3>

        <table style="border-collapse: collapse; width: 100%; font-size:14px;">
            <tr><td><b>Sidewalk score</b></td><td>{row['sidewalk_quality_and_walkability_score']}</td></tr>
            <tr><td><b>Greenery score</b></td><td>{row['greenery_score']}</td></tr>
            <tr><td><b>Enclosure score</b></td><td>{row['building_enclosure_score']}</td></tr>
            <tr><td><b>Comfort score</b></td><td>{row['Comfort_and_perceived_safety_score']}</td></tr>
            <tr><td><b>Comment</b></td><td style="max-width:350px; white-space: normal;">{row['gpt_comment']}</td></tr>
        </table>
    </div>
    """

    container = f"""
    <div style="display:flex; flex-direction:row; align-items:flex-start; margin-bottom:40px;">
        {img_html}
        {table_html}
    </div>
    <hr style="border: 0; height:1px; background:#cccccc; margin:30px 0;">
    """
    
    display(HTML(container))

Total Score: 86

Sidewalk score 23
Greenery score 21
Enclosure score 20
Comfort score 22
Comment With about 32.6% sidewalks and no visible roadway, the image shows a wide, brick-paved pedestrian space with benches and active foot traffic, indicating high walkability and comfort. Roughly 21.1% trees plus additional planting beds provide good shade and greenery, while substantial, well-maintained building facades create a strong, pleasant sense of enclosure.

Total Score: 81

Sidewalk score 22
Greenery score 18
Enclosure score 21
Comfort score 20
Comment With about 39.9% sidewalks and almost no visible roadway, the image shows a wide, continuous, unobstructed sidewalk along active ground-floor shops, which supports good walkability and enclosure. Around 22% trees provide a pleasant canopy and some shade, and the limited roadway presence plus street lighting and clear sightlines contribute to a comfortable, safe-feeling pedestrian environment.

Total Score: 81

Sidewalk score 21
Greenery score 20
Enclosure score 19
Comfort score 21
Comment With about 28% of the scene as paved pedestrian space and no visible roadway, the brick sidewalks and plaza appear wide, continuous, and well-separated from traffic, supporting comfortable walking. Roughly 27% trees plus dense canopy provide shade and greenery, while the historic, well-maintained building facades create a defined, pleasant enclosure that feels safe and calm despite some visual clutter from posts and chains.

Total Score: 80

Sidewalk score 21
Greenery score 18
Enclosure score 22
Comfort score 19
Comment With about 18.1% sidewalks and only 11.5% roadway, the image shows a wide, unobstructed sidewalk along a calm residential street. Roughly 26.6% trees plus landscaped shrubs provide good shade and greenery, while the well-maintained, continuous building facade offers strong enclosure and a comfortable, safe-feeling environment.

Total Score: 78

Sidewalk score 21
Greenery score 18
Enclosure score 20
Comfort score 19
Comment With about 17.6% sidewalks and a visibly wide, continuous, unobstructed pedestrian zone, walkability is strong, supported by active storefronts and street furniture. Roughly 20.7% trees and additional planters provide good shade and greenery, while moderate roadway share (17.6%) and calm traffic conditions contribute to a comfortable, enclosed, and safe-feeling streetscape.

Look at the lowest-scoring points

Click to show code
df_sorted = df.sort_values("total_score")
worst456 = df_sorted.iloc[0:5].copy()

# 地图中心
center_lat = worst456["lat"].mean()
center_lon = worst456["lon"].mean()

# 地图
m_worst456 = folium.Map(
    location=[center_lat, center_lon],
    zoom_start=13,
    width=750,
    height=450,
    tiles="CartoDB Positron"
)

# 添加 marker
for idx, row in worst456.iterrows():

    # base64 图片编码
    with open(row["abs_image_path"], "rb") as f:
        img_base64 = base64.b64encode(f.read()).decode()

    html = f"""
    <h4>Point ID: {row['point_id']}</h4>
    <p>Total Score: {row['total_score']}</p>
    <img src="data:image/jpeg;base64,{img_base64}" width="300">
    """

    iframe = folium.IFrame(html=html, width=330, height=330)
    popup = folium.Popup(iframe, max_width=400)

    folium.CircleMarker(
        location=[row["lat"], row["lon"]],
        radius=7,
        color="#d9534f",      # 红色 → 分数低
        fill=True,
        fill_color="#ff6b6b",
        fill_opacity=0.9,
        tooltip=f"Score: {row['total_score']}",
        popup=popup
    ).add_to(m_worst456)

m_worst456
Make this Notebook Trusted to load map: File -> Trust Notebook
Click to show code
from IPython.display import HTML, display

# 取倒数三名
worst3 = df.nsmallest(5, "total_score").copy()

for idx, row in worst3.iterrows():
    
    img_html = f"""
    <div style="flex: 1; padding-right:20px;">
        <img src="{row['abs_image_path']}" width="420" style="border-radius:8px;"/>
    </div>
    """
    
    table_html = f"""
    <div style="flex: 1.2; font-size:14px;">
        <h3 style="margin-top:0;">Total Score: {row['total_score']}</h3>

        <table style="border-collapse: collapse; width: 100%; font-size:14px;">
            <tr><td><b>Sidewalk score</b></td><td>{row['sidewalk_quality_and_walkability_score']}</td></tr>
            <tr><td><b>Greenery score</b></td><td>{row['greenery_score']}</td></tr>
            <tr><td><b>Enclosure score</b></td><td>{row['building_enclosure_score']}</td></tr>
            <tr><td><b>Comfort score</b></td><td>{row['Comfort_and_perceived_safety_score']}</td></tr>
            <tr><td><b>Comment</b></td><td style="max-width:350px; white-space: normal;">{row['gpt_comment']}</td></tr>
        </table>
    </div>
    """

    container = f"""
    <div style="display:flex; flex-direction:row; align-items:flex-start; margin-bottom:40px;">
        {img_html}
        {table_html}
    </div>
    <hr style="border: 0; height:1px; background:#cccccc; margin:30px 0;">
    """
    
    display(HTML(container))

Total Score: 15

Sidewalk score 4
Greenery score 0
Enclosure score 6
Comfort score 5
Comment Segmentation shows very little sidewalk (1.1%) and roadway (0.6%), which matches the image of a narrow underpass with a constrained, elevated sidewalk and no clear pedestrian crossings or amenities. With almost no greenery (0% trees/grass) and harsh, tunnel-like walls providing enclosure but also a dark, vehicle-oriented environment, overall comfort and perceived safety for walking are low.

Total Score: 20

Sidewalk score 3
Greenery score 1
Enclosure score 10
Comfort score 6
Comment With 0% sidewalks in the segmentation and visible construction barriers, cones, and highway-like conditions, this area offers very poor walkability and feels dominated by cars. The 20.9% building facades provide some enclosure but are set back and separated by wide roadway (12.1%) and work zones, while greenery is almost absent and the environment appears exposed and uncomfortable for pedestrians.

Total Score: 21

Sidewalk score 6
Greenery score 0
Enclosure score 8
Comfort score 7
Comment With only about 1.4% sidewalk and a wide, vehicle-oriented gas station forecourt, walking space is minimal, broken up by driveways, and dominated by parked and moving cars. The scene has virtually no trees or grass (0% greenery) and limited building frontage (13.3% facades), so enclosure and comfort are low despite clear visibility and daylight.

Total Score: 22

Sidewalk score 2
Greenery score 10
Enclosure score 6
Comfort score 4
Comment Segmentation shows 22.4% roadway and 0% sidewalks, which matches the image of a narrow curb edge directly beside a blank wall with no usable pedestrian path. While there is substantial tree coverage (31.2%) hanging over the wall providing some greenery, the high, featureless concrete wall, lack of active frontages, and car-oriented roadway make the space feel uncomfortable and unsafe for walking.

Total Score: 23

Sidewalk score 3
Greenery score 6
Enclosure score 8
Comfort score 6
Comment With only about 1.9% sidewalks and a visually narrow, obstructed edge behind parked cars and barriers, walkability is very poor. Limited trees (6.4%) and some tall but worn buildings and infrastructure create a harsh, car-dominated environment that feels exposed and not very comfortable for pedestrians.

Click to show code
import textwrap
import plotly.express as px

# 1. 先生成一个短一点、自动换行的 comment 列
def wrap_comment(text, width=40, max_lines=4):
 import textwrap

def wrap_comment_full(text, width=40):
    """
    对 gpt_comment 自动换行,但不截断内容:
    - 每行大约 width 个字符
    - 不限制行数
    - 用 <br> 让 Plotly hover 里换行
    """
    if not isinstance(text, str):
        return ""
    wrapped = textwrap.wrap(text, width=width)
    return "<br>".join(wrapped)

df["gpt_comment_full"] = df["gpt_comment"].apply(
    lambda t: wrap_comment_full(t, width=50)
)


df["gpt_comment_short"] = df["gpt_comment"].apply(
    lambda t: wrap_comment(t, width=40, max_lines=4)
)

# 2. 为点大小准备一个平方列(只针对 total_score)
df["total_score_squared"] = df["total_score"] ** 2
NEW_SIZE_MAX = 30

# 3. 只画 total_score 这一张图
fig_total = px.scatter_map(
    df,
    lat="lat",
    lon="lon",
    color="total_score",              # 颜色用原始 total score
    color_continuous_scale="Viridis",
    size="total_score_squared",       # 点大小用平方后的分数
    size_max=NEW_SIZE_MAX,
    zoom=13,
    map_style="carto-positron",
    hover_data={
        "total_score_squared": False,
        "total_score": True,          # 显示总分
        "street": True,
        "gpt_comment_full": True,    # 显示处理后的简短 comment
        "gsv_url_sidewalk": False,     # 显示街景链接(可点击)
        "lat": False,
        "lon": False,
        "gpt_comment": False          # 不显示原始长 comment
    }
)

fig_total.update_layout(
    title="Street-level Images: Walkability Score Map with LLM Comments",
    margin={"r": 0, "t": 40, "l": 0, "b": 0},
    height=600
)

# 4. 导出为独立 HTML,方便封面用 iframe 嵌入
fig_total.write_html(
    "map_total_score_with_ai_comment.html",
    include_plotlyjs="cdn",
    full_html=True
)

fig_total.show()

Result

I want to end by going back to the basic question of this project. On a map a street can look very walkable. From the sidewalk it can feel very different. My goal was to see whether AI can help us measure that feeling along three real streets in Philadelphia.

To do this I followed three simple steps. First, I built a regular 250 meter sampling along Market Street, Chestnut Street and Walnut Street and collected street view images for each point. Second, I used semantic segmentation to break each image into basic elements and to calculate the share of sidewalk, roadway, buildings, sky and greenery. Third, I asked a large language model to look at each scene and to give four scores for sidewalk quality, greenery, enclosure and comfort and safety, which sum to a total walkability score from 0 to 100.

The results are not perfect, but they make sense. Higher sidewalk and greenery shares usually come with higher walkability scores. Large exposed roadway often comes with lower comfort and safety. On the maps, many of the lowest scores appear at places like bridge ramps, underpasses and gas stations. These are exactly the spots that also feel unpleasant or unsafe to us as pedestrians.

For me the main message is that AI already has enough perceptual ability to do a reasonable first pass on streetscape walkability. It can read visual cues in a way that is similar to human judgment. It can also do this very quickly and at scale, across hundreds or thousands of scenes, which was very hard with manual ratings or custom models in the past.

This does not mean AI replaces fieldwork or community input. It still has biases and blind spots and it does not know local context. But it can be a useful screening tool that helps us see patterns along long corridors and identify segments that deserve closer attention.

Looking ahead, I think the more interesting question is not only whether AI can see the street, but how we choose to use that ability. If we combine AI’s fast, city scale perception with human experience and community voices, we can move closer to designing streets that are not only “walkable” on paper, but actually feel walkable to the people who use them every day.